#pragma once

#include <iostream>
#include <string>
#include <functional>
#include "Common.hpp"
#include "Connection.hpp"

#define SIZE 1024

class Channel : public Connection
{
public:
    Channel(int sockfd, InetAddr &client)
        : _sockfd(sockfd),
          _client_addr(client)
    {
        SetNonBlock(sockfd);
    }

    void Recver()
    {
        char buffer[SIZE];
        while(true)
        {
            buffer[0] = 0;
            ssize_t n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if(n > 0)
            {
                buffer[n] = 0;
                _inbuffer += buffer;
                continue;
            }
            else if(n == 0)
            {
                Excepter();
                return;
            }
            else
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    Excepter();
                    return;
                }
            }
        }

        if(!_inbuffer.empty())
        {
            _outbuffer += _handler(_inbuffer);
        }

        if(!_outbuffer.empty())
        {
            //Sender();
            GetOwner()->EnableReadWrite(_sockfd, true, true);
        }
    }

    void Sender()
    {
        while(true)
        {
            ssize_t n = send(_sockfd, _outbuffer.c_str(), _outbuffer.size(), 0);
            if(n > 0)
            {
                _outbuffer.erase(0, n);
                if(_outbuffer.empty())
                {
                    break;
                }
                else if (n == 0)
                {
                    break;
                }
                
                else
                {
                    if(errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    if(errno == EINTR)
                        continue;
                    else
                    {
                        Excepter();
                        return;
                    }
                }
            }
        }

        if(!_outbuffer.empty())
        {
            GetOwner()->EnableReadWrite(_sockfd, true, true);
        }
        else
        {
            GetOwner()->EnableReadWrite(_sockfd, true, false);
        }
    }

    void Excepter()
    {
        GetOwner()->DelConnection(_sockfd);
    }

    int GetSockFd()
    {
        return _sockfd;
    }

    std::string &Inbuffer()
    {
        return _inbuffer;
    }

    void AppendOutBuffer(const std::string &out)
    {
        _outbuffer += out;
    }

    ~Channel()
    {
    }

private:
    int _sockfd;
    std::string _inbuffer;
    std::string _outbuffer;

    InetAddr _client_addr;
    handler_t _handler;
};